﻿using System;
using System.Collections.Generic;

namespace Eventable
{
    public interface IEventProvider
    {
        /// <summary>
        /// Выводит список зарегистрированных событий
        /// </summary>
        /// <returns></returns>
        IEnumerable<string> GetDeclaredEvents();

        /// <summary>
        /// Прослушивание события
        /// </summary>
        /// <param name="keyObj">Объект ключ</param>
        /// <param name="handler">Делегат прослушки</param>
        /// <param name="nameEvents">Наименование события</param>
        /// <returns></returns>
        void ListenEvent(object keyObj, EvntHandler handler, params string[] nameEvents);

        /// <summary>
        /// Отключить прослушивание события 
        /// (отключается одноразовое подключение, при множественном - оставшиеся делегаты не будут затронуты)
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="handler">Делегат прослушки</param>
        /// <returns>Успешность операции</returns>
        void UnlistenEvent(object keyObj, EvntHandler handler, params string[] nameEvents);

        /// <summary>
        /// Отключить прослушивание событий для данного объекта
        /// (отключается одноразовое подключение, при множественном - оставшиеся делегаты не будут затронуты)
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="handler">Делегат прослушки</param>
        /// <returns>Успешность операции</returns>
        void UnlistenEvent(object keyObj, params string[] nameEvents);

        /// <summary>
        /// Отключить все прослушивания события для данного объекта
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="handler">Делегат обратного вызова</param>
        /// <returns>Успешность операции</retur
        void DettachObject(object keyObj);

        /// <summary>
        /// Оповещение о событии
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="args">Параметры события</param>
        /// <returns>Бинарная реакция</returns>
        bool NotifyEvent(string nameEvent, params object[] args);

        /// <summary>
        /// Оповещение о событии с перехватом исключения (если такое возникнет, то вернуться false в любом случае)
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="ex">Перехваченное исключение</param>
        /// <param name="args">Параметры события</param>
        /// <returns>Бинарная реакция</returns>
        bool TryNotifyEvent(string nameEvent, out Exception ex, params object[] args);

        /// <summary>
        /// Оповещение о событии c глушением исключения (если такое возникнет, то вернуться false в любом случае)
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>        
        /// <param name="args">Параметры события</param>
        /// <returns>Бинарная реакция</returns>
        bool TryNotifyEvent(string nameEvent, params object[] args);

        /// <summary>
        /// Оповещение о событии c глушением исключения (если такое возникнет, то вернётся false в любом случае, при этом будут отработаны все делегаты)
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="args">Параметры события</param>
        /// <returns>Бинарная реакция</returns>
        bool TryAllNotifyEvent(string nameEvent, params object[] args);

        /// <summary>
        /// Оповещение о событии c глушением исключения (если такое возникнет, то вернётся false в любом случае, при этом будут отработаны все делегаты)
        /// </summary>
        /// <param name="nameEvent">Наименование события</param>
        /// <param name="exs"></param>
        /// <param name="args">Параметры события</param>
        /// <returns>Бинарная реакция</returns>
        bool TryAllNotifyEvent(string nameEvent, out Exception[] exs, params object[] args);
    }

    public class NullEventProvider : IEventProvider
    {
        NullEventProvider()
        {

        }
        public static readonly NullEventProvider Singleton = new NullEventProvider();
        #region IEventProvider
        public IEnumerable<string> GetDeclaredEvents()
        {
            if (false)
                yield return null;
        }

        public void ListenEvent(object keyObj, EvntHandler handler, params string[] nameEvents)
        {
           
        }

        public void UnlistenEvent(object keyObj, EvntHandler handler, params string[] nameEvents)
        {
        }

        public void UnlistenEvent(object keyObj, params string[] nameEvents)
        {
        }

        public void DettachObject(object keyObj)
        {
        }

        public bool NotifyEvent(string nameEvent, params object[] args)
        {
            return true;
        }

        public bool TryNotifyEvent(string nameEvent, out Exception ex, params object[] args)
        {
            ex = null;
            return true;
        }

        public bool TryNotifyEvent(string nameEvent, params object[] args)
        {            
            return true;
        }

        public bool TryAllNotifyEvent(string nameEvent, params object[] args)
        {
            return true;
        }
        #endregion


        public bool TryAllNotifyEvent(string nameEvent, out Exception[] exs, params object[] args)
        {
            exs = new Exception[0];
            return true;
        }
    }

	/// <summary>
	/// Дескриптор метода обработчика события
	/// </summary>
	/// <param name="sender">Генератор события</param>
	/// <param name="nameEvent"></param>
	/// <param name="args">Аргументы события</param>
	/// <returns></returns>
	public delegate bool EvntHandler(object sender, string nameEvent, params object[] args);

    public delegate T EventParamSelector<T>(object sender, string nameEvent, params object[] args);

}
